﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TorchView.GameContent.Data;
using System.ComponentModel;

namespace TorchView.GameContent.Skills
{
  public abstract class SkillBase : IDataSerializable
  {
    #region Defaults
    const float Range_Default = 0f;
    const float ManaCost_Default = 0f;
    const float Speed_Default = 1f;
    const float RangeMin_Default = 0f;
    const float FindTargetAngle_Default = 0f;
    const float ManaCostOT_Default = 0f;
    const bool ContinuousLooping_Default = false;
    const int MinimumTimeMs_Default = 0;
    const int CoolDownMS_Default = 0;
    const int DurationOverrideMS_Default = 0;
    const int SlowdownMS_Default = 0;
    const bool RequiresDW_Default = false;
    const bool SingleTarget_Default = false;
    const int LevelRequired_Default = 1;
    #endregion

    internal SkillBase()
    {
      Decendants = new List<IDataSerializable>(12);
    }

    protected abstract string GetBlockName();

    #region IDataSerializable Members

    public DataBlock MakeDataBlock()
    {
      var block = Data == null ? new DataBlock(GetBlockName()) : Data;
      FillBlock(block);
      return block;
    }

    protected virtual void FillBlock(DataBlock block)
    {
      block.SetPropertyValue("DISPLAYNAME", SkillDisplayName, PropertyType.TRANSLATE);
      block.SetPropertyValue("DESCRIPTION", Description, PropertyType.TRANSLATE);
      block.SetPropertyValue("TRIGGER", Trigger);
      block.SetPropertyValue("SKILL_ICON", Icon);
      block.SetPropertyValue("SKILL_ICON_INACTIVE", InactiveIcon);
      block.SetPropertyValue("TARGET_TYPE", TargetType);
      block.SetPropertyValue("ANIMATION", Animation);
      block.SetPropertyValue("ANIMATIONDW", AnimationDW);
      block.SetPropertyValue("ANIMATIONDWLOOP", AnimationDWLoop);
      block.SetPropertyValue("ANIMATIONLOOP", AnimationLoop);
      block.SetOrClearPropertyValue(Range != Range_Default, "RANGE", Range);
      block.SetOrClearPropertyValue(RangeMin != RangeMin_Default, "RANGEMIN", RangeMin);
      block.SetOrClearPropertyValue(ManaCost != ManaCost_Default, "MANACOST", ManaCost);
      block.SetOrClearPropertyValue(ManaCostOT != ManaCostOT_Default, "MANACOSTOT", ManaCostOT);
      block.SetOrClearPropertyValue(Speed != Speed_Default, "SPEED", Speed);
      block.SetOrClearPropertyValue(ContinuousLooping != ContinuousLooping_Default, "CONTINUOUSLOOPING", ContinuousLooping);
      block.SetOrClearPropertyValue(MinimumTimeMs != MinimumTimeMs_Default, "MINIMUMTIMEMS", MinimumTimeMs);
      block.SetOrClearPropertyValue(CoolDownMS != CoolDownMS_Default, "COOLDOWNMS", CoolDownMS);
      block.SetOrClearPropertyValue(FindTargetAngle != FindTargetAngle_Default, "FINDTARGETANGLE", FindTargetAngle);
      block.SetOrClearPropertyValue(DurationOverrideMS != DurationOverrideMS_Default, "DURATIONOVERRIDEMS", DurationOverrideMS);
      block.SetOrClearPropertyValue(SlowdownMS != SlowdownMS_Default, "SLOWDOWNMS", SlowdownMS);
      block.SetPropertyValue("THEMEREQUIRED", RequiredTheme);
      block.SetPropertyValue("REQUIREMENT_RIGHT", RightWeaponReq);
      block.SetPropertyValue("REQUIREMENT_LEFT", LeftWeaponReq);
      block.SetOrClearPropertyValue(RequiresDW != RequiresDW_Default, "DUELWIELD_REQUIRED", RequiresDW);
      block.SetOrClearPropertyValue(SingleTarget != SingleTarget_Default, "SINGLETARGET", SingleTarget);
      block.SetOrClearPropertyValue(LevelRequired != LevelRequired_Default, "LEVEL_REQUIRED", LevelRequired);

      block.SubBlocks.Clear();
      block.SubBlocks.AddRange(Decendants.Select(d => d.MakeDataBlock()));
    }

    public virtual void LoadFromDataBlock(DataBlock block)
    {
      Data = block;
      SkillDisplayName = block.GetPropertyValue("DISPLAYNAME", "");
      Description = block.GetPropertyValue("DESCRIPTION", "");
      Animation = block.GetPropertyValue("ANIMATION", "");
      Trigger = block.GetPropertyValue("TRIGGER", "");
      Icon = block.GetPropertyValue("SKILL_ICON", "");
      InactiveIcon = block.GetPropertyValue("SKILL_ICON_INACTIVE", "");
      Range = block.GetPropertyValue("RANGE", Range_Default);
      ManaCost = block.GetPropertyValue("MANACOST", ManaCost_Default);
      Speed = block.GetPropertyValue("SPEED", Speed_Default);
      TargetType = block.GetPropertyValue("TARGET_TYPE", "");
      RangeMin = block.GetPropertyValue("RANGEMIN", RangeMin_Default);
      FindTargetAngle = block.GetPropertyValue("FINDTARGETANGLE", FindTargetAngle_Default);
      ManaCostOT = block.GetPropertyValue("MANACOSTOT", ManaCostOT_Default);
      ContinuousLooping = block.GetPropertyValue("CONTINUOUSLOOPING", ContinuousLooping_Default);
      MinimumTimeMs = block.GetPropertyValue("MINIMUMTIMEMS", MinimumTimeMs_Default);
      CoolDownMS = block.GetPropertyValue("COOLDOWNMS", CoolDownMS_Default);
      DurationOverrideMS = block.GetPropertyValue("DURATIONOVERRIDEMS", DurationOverrideMS_Default);
      SlowdownMS = block.GetPropertyValue("SLOWDOWNMS", SlowdownMS_Default);
      AnimationDW = block.GetPropertyValue("ANIMATIONDW", "");
      AnimationDWLoop = block.GetPropertyValue("ANIMATIONDWLOOP", "");
      AnimationLoop = block.GetPropertyValue("ANIMATIONLOOP", "");
      RequiredTheme = block.GetPropertyValue("THEMEREQUIRED", "");
      RightWeaponReq = block.GetPropertyValue("REQUIREMENT_RIGHT", "");
      LeftWeaponReq = block.GetPropertyValue("REQUIREMENT_LEFT", "");
      RequiresDW = block.GetPropertyValue("DUELWIELD_REQUIRED", RequiresDW_Default);
      SingleTarget = block.GetPropertyValue("SINGLETARGET", SingleTarget_Default);
      LevelRequired = block.GetPropertyValue("LEVEL_REQUIRED", LevelRequired_Default);
      
      Decendants.Clear();
      Decendants.AddRange(block.SubBlocks.Select(GetDecendant).Where(b => b != null));
    }

    #endregion

    protected virtual IDataSerializable GetDecendant(DataBlock block)
    {
      switch (block.Name)
      {
        case SkillAffixes.BLOCK_NAME:
          SkillAffixes affixes = new SkillAffixes();
          affixes.LoadFromDataBlock(block);
          return affixes;
        case SkillAffixesRemove.BLOCK_NAME:
          SkillAffixesRemove affixesRemove = new SkillAffixesRemove();
          affixesRemove.LoadFromDataBlock(block);
          return affixesRemove;
        case SkillEffects.BLOCK_NAME:
          SkillEffects effects = new SkillEffects();
          effects.LoadFromDataBlock(block);
          return effects;
        case SkillEffectsRemove.BLOCK_NAME:
          SkillEffectsRemove effectsRemove = new SkillEffectsRemove();
          effectsRemove.LoadFromDataBlock(block);
          return effectsRemove;
        default:
          break;
      }
      if (block.Name.StartsWith(SkillEvent.BLOCK_NAME))
      {
        SkillEvent skillEvent = new SkillEvent();
        skillEvent.LoadFromDataBlock(block);
        return skillEvent;
      }
      return null;
    }

    public DataBlock Data { get; set; }

    public List<IDataSerializable> Decendants { get; private set; }

    #region Strings
    [Description("Display name of Skill"), Category("~Skill Strings"), Browsable(true), DisplayName("Display Name")]
    public string SkillDisplayName { get; set; }

    [Category("~Skill Strings"), Description("Description of Skill"), DisplayName("Description")]
    public string Description { get; set; }
    #endregion

    #region Animation
    [DisplayName("Animation"), Description("Animation to use"), Category("Animation")]
    public string Animation { get; set; }

    [Category("Animation"), DisplayName("Dual Wield Animation"), Description("Dual Wield Animation")]
    public string AnimationDW { get; set; }

    [Category("Animation"), Description("Looping Duel Wield Animation"), DisplayName("Loop DW Animation")]
    public string AnimationDWLoop { get; set; }

    [Category("Animation"), DisplayName("Loop Animation"), Description("Looping Animation")]
    public string AnimationLoop { get; set; }

    [Category("Animation"), Description("Animation Trigger"), DisplayName("Trigger")]
    public string Trigger { get; set; }
    #endregion

    #region Requirements
    [Description("Theme required for skill"), DisplayName("Theme Required"), Category("Requirements")]
    public string RequiredTheme { get; set; }

    [DisplayName("Req Duel Wield"), Category("Requirements"), Description("Skill requires duel wielding")]
    public bool RequiresDW { get; set; }

    [Description("Right Hand Weapon Req"), Category("Requirements"), DisplayName("Right Req.")]
    public string RightWeaponReq { get; set; }

    [Category("Requirements"), Description("Left Hand Weapon Req"), DisplayName("Left Req.")]
    public string LeftWeaponReq { get; set; }

    [Description("Required level for skill"), Category("Requirements"), DisplayName("Level Required")]
    public int LevelRequired { get; set; }

    #endregion

    #region Skill

    [Category("Skill"), Description("Skill loops until stopped?"), DisplayName("Continuous Looping")]
    public bool ContinuousLooping { get; set; }

    [Category("Skill"), Description("Cool down for skill (milliseconds)"), DisplayName("Cool down")]
    public int CoolDownMS { get; set; }

    [Category("Skill"), Description("Duration Override (milliseconds)"), DisplayName("Duration Override")]
    public int DurationOverrideMS { get; set; }

    [DisplayName("Find Target Angle"), Description("Angle in which to find targets"), Category("Skill")]
    public float FindTargetAngle { get; set; }

    [Category("Skill"), DisplayName("Icon"), Description("Skill Icon")]
    public string Icon { get; set; }

    [DisplayName("Inactive Icon"), Category("Skill"), Description("Skill Inactive Icon")]
    public string InactiveIcon { get; set; }

    [Description("Mana cost of Skill"), DisplayName("Mana Cost"), Category("Skill")]
    public float ManaCost { get; set; }

    [Category("Skill"), Description("Mana cost Over Time"), DisplayName("Mana Cost OT")]
    public float ManaCostOT { get; set; }

    [Description("Minimum time for Looping skill (in miliseconds)"), Category("Skill"), DisplayName("Minimum Time")]
    public int MinimumTimeMs { get; set; }

    [Description("Range of Skill"), DisplayName("Range"), Category("Skill")]
    public float Range { get; set; }

    [Category("Skill"), DisplayName("Range Min"), Description("Minimum Range of Skill")]
    public float RangeMin { get; set; }

    [Category("Skill"), DisplayName("Single Target"), Description("Skill is Single Target")]
    public bool SingleTarget { get; set; }

    [Category("Skill"), Description("Slow down duration (milliseconds)"), DisplayName("Slowdown Duration")]
    public int SlowdownMS { get; set; }

    [DisplayName("Speed"), Description("Animation Speed"), Category("Skill")]
    public float Speed { get; set; }

    [DisplayName("Target Type"), Category("Skill"), Description("Skill Target Type")]
    public string TargetType { get; set; }

    #endregion

    public override string ToString()
    {
      return GetBlockName();
    }
  }
}
